#include "Mesh_PCH.h"
#include "Model.h"

/*--------------------------------------------------------------------------*/

Model::Model(const std::string& path) :
	m_path(path)
{
	std::string ext = ::GetFileExt(m_path);

	if (ext == ".3ds")
	{
		LoadMeshFrom3DS();
	}
	else
	{
		__Check(false);
	}
}

/*--------------------------------------------------------------------------*/

Model::~Model()
{

}

/*--------------------------------------------------------------------------*/

const std::string& Model::GetPath() const
{
	return m_path;
}

/*--------------------------------------------------------------------------*/

uint Model::GetSurfaceNum() const
{
	return m_surfaces.size();
}

/*--------------------------------------------------------------------------*/

ISurface* Model::GetSurface( uint i )
{
	__Check(m_surfaces.size() > i);
	return m_surfaces[i];
}

/*--------------------------------------------------------------------------*/

void Model::LoadMeshFrom3DS()
{
	Lib3dsFile* model = lib3ds_file_load(m_path.c_str());
	__Check(model);
	uint id = 0;

	for (Lib3dsMesh* mesh = model->meshes; mesh != NULL; mesh = mesh->next)
	{
		uint numVertices = mesh->points;
		uint numFaces = mesh->faces;
		uint numIndices = mesh->faces * 3;
		
		MeshSurface* surface = new MeshSurface(this, id++, EVertex::Indices | EVertex::Position0 | EVertex::Normal0 | EVertex::TexCoord0, numVertices, numIndices*4);

		glm::vec3* position0	= surface->vPosition0();
		glm::vec3* normal0		= surface->vNormal0();
		glm::vec2* texcoord0	= surface->vTexcoord0();
		ushort* indices			= surface->GetIndices();

		for (uint i = 0; i < numVertices; ++i)
		{
			position0[i].x	= mesh->pointL[i].pos[0];
			position0[i].y	= mesh->pointL[i].pos[1];
			position0[i].z	= mesh->pointL[i].pos[2];

			normal0[i].x	= 0.0f;
			normal0[i].y	= 0.0f;
			normal0[i].z	= 0.0f;

			texcoord0[i].x	= mesh->texelL[i][0];
			texcoord0[i].y	= mesh->texelL[i][1];
		}

		for (uint i = 0; i < numFaces; ++i)
		{
			indices[i * 3 + 0] = mesh->faceL[i].points[0];
			indices[i * 3 + 1] = mesh->faceL[i].points[1];
			indices[i * 3 + 2] = mesh->faceL[i].points[2];

			ushort a = mesh->faceL[i].points[0];
			ushort b = mesh->faceL[i].points[1];
			ushort c = mesh->faceL[i].points[2];

			glm::vec3 facenormal = glm::cross(glm::normalize(position0[a] - position0[b]), glm::normalize(position0[a] - position0[c]));
			normal0[a] += facenormal;
			normal0[b] += facenormal;
			normal0[c] += facenormal;
		}

		for (uint i = 0; i < numVertices; ++i)
		{
			normal0[i] = glm::normalize(normal0[i]);
		}

		m_surfaces.push_back(surface);

// 		/*----generowanie VB--------------------------------------------------------*/
// 
// 		GLVertexBuffer* VB = ROpenGL::GenBuffer("VB:" + this->m_path + "/MESH:" + mesh->name, EVertexDeclaration::BasicVertex, mesh->faces * 3);
// 
// 		BasicVertex* ptnVB = (BasicVertex*) ROpenGL::MapBuffer(VB);
// 		uint vert = 0;
// 
// 		for (uint cur_face = 0; cur_face < mesh->faces; ++cur_face)
// 		{
// 			Lib3dsFace* face = &mesh->faceL[cur_face];
// 
// 			for (uint i = 0; i < 3; i++)
// 			{
// 				memcpy(&ptnVB[vert].position, &mesh->pointL[face->points[ i ]].pos, sizeof(Lib3dsVector));
// 				ptnVB[vert].position *= this->m_scaling;
// 
// 				memcpy(&ptnVB[vert].texcoord, mesh->texelL[face->points[ i ]], sizeof(Lib3dsTexel));
// 				memcpy(&ptnVB[vert].normal, &normals[vert], sizeof(Lib3dsVector));
// 				vert++;
// 			}
// 		}
// 
// 		//
// 		// 		for (uint vert = 0; vert < mesh->points; ++vert)
// 		// 		{
// 		// 			glm::vec3* v = (glm::vec3*) mesh->pointL[vert].pos;
// 		// 			glm::vec3* t = (glm::vec3*) mesh->texelL[vert];
// 		//
// 		// 			ptnVB[vert].texcoord = *t;
// 		// 			ptnVB[vert].position = (*v * this->m_scaling);
// 		//
// 		// 			ptnVB[vert].normal.x = normals[vert][0];
// 		// 			ptnVB[vert].normal.y = normals[vert][1];
// 		// 			ptnVB[vert].normal.z = normals[vert][2];
// 		// 		}
// 
// 		ROpenGL::UnmapBuffer(VB);
// 		delete [] normals;
// 
// 		/*--------generowanie IB----------------------------------------------------*/
// 		//
// 		// 		GLVertexBuffer* IB = ROpenGL::GenBuffer("IB:" + this->m_path + "/MESH:" + mesh->name, GL_ELEMENT_ARRAY_BUFFER, mesh->faces * 3 * sizeof(uint), GL_STATIC_DRAW);
// 		// 		uint* ptnIB = (uint*) ROpenGL::MapBuffer(IB);
// 		//
// 		// 		for (uint cur_face = 0; cur_face < mesh->faces; ++cur_face)
// 		// 		{
// 		// 			Lib3dsFace* face = &mesh->faceL[cur_face];
// 		//
// 		// 			ptnIB[3 * cur_face + 0] = (uint) face->points[0];
// 		// 			ptnIB[3 * cur_face + 1] = (uint) face->points[1];
// 		// 			ptnIB[3 * cur_face + 2] = (uint) face->points[2];
// 		// 		}
// 		//
// 		// 		ROpenGL::UnmapBuffer(IB);
// 
// 		/*-------generowanie VAO----------------------------------------------------*/
// 
// 		GLVertexArray* VAO = ROpenGL::GenVertexArray("VAO:" + this->m_path + "/MESH:" + mesh->name, GL_TRIANGLES, mesh->faces * 3);
// 		ROpenGL::BindVertexArray(VAO);
// 
// 		ROpenGL::AttachBuffer(VB);
// 		//ROpenGL::AttachIndexBuffer(IB);
// 
// 		ROpenGL::UnbindVertexArray();
// 
// 		ROpenGL::UniqueBufferOwner(VAO, VB);
// 		//		ROpenGL::UniqueBufferOwner(VAO, IB);
// 
// 		/*-------pobieranie materialu-----------------------------------------------*/
// 
// 		Lib3dsMaterial* meshMaterial = NULL;
// 
// 		for (Lib3dsMaterial* material = model->materials; material != NULL; material = material->next)
// 		{
// 			if (std::strcmp(mesh->faceL[0].material, material->name) == 0)
// 			{
// 				meshMaterial = material;
// 				break;
// 			}
// 		}
// 
// 		AssertLogC(meshMaterial == NULL, "Mesh %s is without assigned material.", mesh->name);
// 
// 		/*--------upychanie materialu do enginowej obudowy--------------------------*/
// 
// 		std::stringstream ss;
// 		ss << "material_" << this->m_path << "_" << mesh->name;
// 
// 		// 		Material* material = new Material();
// 		// 		g_Material->AddMaterial(ss.str(), material);
// 		//		delete material;
// 		//		material = const_cast<Material*>(g_Material->GetMaterial(ss.str()));
// 
// 		/*--------------------------------------------------------------------------*/
// 		//
// 		// 		ShaderRes* shader = NULL;
// 		// 		g_Resources->Get("ds_firstPassShader", &shader);
// 		// 		material->SetShader(shader->GetShader());
// 		//
// 		// 		g_Resources->Get("ds_firstPassInstancedShader", &shader);
// 		// 		material->SetInstancedShader(shader->GetShader());
// 		//
// 		// 		material->SetShininess(100.0f);
// 		// 		material->SetBumpfactor(0.0f);
// 		//
// 		// 		/*---------uzupelnianie materialu danymi z 3ds------------------------------*/
// 		//
// 		// 		material->SetAmbient(Color(meshMaterial->ambient[0],
// 		// 		                           meshMaterial->ambient[1],
// 		// 		                           meshMaterial->ambient[2],
// 		// 		                           meshMaterial->ambient[3]));
// 		//
// 		// 		material->SetDiffuse(Color(meshMaterial->diffuse[0],
// 		// 		                           meshMaterial->diffuse[1],
// 		// 		                           meshMaterial->diffuse[2],
// 		// 		                           meshMaterial->diffuse[3]));
// 		//
// 		// 		material->SetSpecular(Color(meshMaterial->specular[0],
// 		// 		                            meshMaterial->specular[1],
// 		// 		                            meshMaterial->specular[2],
// 		// 		                            meshMaterial->specular[3]));
// 		//
// 		// 		material->SetShininess(meshMaterial->shininess);
// 
// 		/*-----------laczenie materialu z vao---------------------------------------*/
// 
// 		MaterialVaoSet mvset(NULL, VAO);
// 
// 		/*--------doczytanie tekstur do materialu-----------------------------------*/
// 
// 		// 		if (meshMaterial->texture1_map.name != "")
// 		// 		{
// 		// 			std::stringstream sss;
// 		// 			sss << ::GetFilePath(this->m_path) << meshMaterial->texture1_map.name;
// 		//
// 		// 			//LogI(1) << "Loading material for " << this->GetPath() << "@" << mesh << " texture1: " << meshMaterial->texture1_map.name << " texture2: " << meshMaterial->texture2_map.name;
// 		//
// 		// 			GLTexture* t = ROpenGL::GetTexture(sss.str());
// 		//
// 		// 			if (t == NULL)
// 		// 			{
// 		// 				t = ROpenGL::GenTexture(sss.str());
// 		// 				ROpenGL::LoadTextureFromFile(t, sss.str());
// 		// 			}
// 		//
// 		// 			TextureRes* q;
// 		// 			g_Resources->Get("grass1", &q);
// 		//
// 		// 			material->AddTexture(q->GetTexture(), "texture0");
// 		// 			material->AddTexture(q->GetTexture(), "bumpmapping");
// 		// 		}
// 
// 		this->m_sets.push_back(mvset);
	}

	lib3ds_file_free(model);
	model = NULL;
}

/*--------------------------------------------------------------------------*/

uint Model::GetTotalSize()
{
	uint size = 0;

	for (uint i = 0; i < m_surfaces.size(); ++i)
	{
		size += m_surfaces[i]->GetSize();
	}

	return size;
}

/*--------------------------------------------------------------------------*/
